<template>
	<el-dialog title="导入坐席账号" :visible.sync="showDialog" :before-close="cancel" width="10rem">
		<div>
			<span>填写模版文件，表头不可修改及删除</span>
			<el-link type="primary" style="padding: 0 5px" :underline="false" :href="excelUrl" target="_blank">
				<el-button class="download-button"><i class="el-icon-download el-icon--left"></i>下载模版</el-button>
			</el-link>
		</div>
		<br />
		<el-upload
			v-loading="loading"
			element-loading-text="数据校验中..."
			element-loading-spinner="el-icon-loading"
			element-loading-background="rgba(255, 255, 255, 0.8)"
			accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
			ref="uploadRef"
			:on-remove="onRemoveUploadFile"
			:on-change="onFileChange"
			@drop.native="e => dragToUpload(e)"
			:file-list="uploadFileList"
			class="upload-demo"
			style="align-items: center; display: flex; flex-direction: column"
			drag
			:auto-upload="false"
			:on-progress="onUploadProgress"
			:action="uploadFileConfig.url"
			:headers="uploadFileConfig.headers"
			:show-file-list="false"
			:on-exceed="onAnotherFile"
			limit="1"
		>
			<div class="el-upload__tip" slot="tip"></div>
			<div class="el-upload__trigger before-upload-box" v-if="uploadFileList.length === 0" slot="trigger">
				<i class="el-icon-excel"></i>
				<div class="el-upload__text upload-text">点击或拖拽上传，仅支持Excel文件，不超过1000行</div>
			</div>
			<div class="upload-box" v-else slot="trigger">
				<div class="upload-items">
					<i class="el-icon-excel excel-img"></i>
					<div class="el-upload__text excel-name">{{ this.uploadFileList[0].name }}</div>
					<i class="el-icon-close excel-remove" @click="removeUploadFile($event)"></i>
				</div>
			</div>
		</el-upload>
		<span v-show="uploadFileList.length === 0 && clickedSubmit" style="color: red">请选择导入文件</span>
		<br />
		<div class="btns">
			<el-button @click="cancel">取消</el-button>
			<el-button type="primary" @click="submit($event)">确认</el-button>
		</div>
	</el-dialog>
</template>

<script>
import { mapState } from 'vuex';
import { createAccount } from '../../../api/api';
import { fixUploadPercent, forceBtnBlur } from '@/utils/MyVueUtil';
import FormReg from '@/utils/regexp';
import XLSX from 'xlsx';
import { getUploadFileConfig, getRoleList, getAllGroups, importAccounts, getBizLabelList, getUserList } from '../../../api/api';
import { getValidateUserNameResult, getValidateRealNameResult, getValidatePassWordResult, getValidateRoleNameResult } from '@/utils/formValidate';

export default {
	components: {},
	props: {
		show: {
			type: Boolean,
			default: false
		},
		roleList: {
			type: Array,
			default: () => []
		},
		// 是否是机构管理员
		isOrgAdmin: {
			type: Boolean,
			default: false
		}
	},
	data() {
		var validateAgentName = (rule, value, callback) => {
			if (value.replace(FormReg.reg9, '').length !== 0) {
				callback(new Error('姓名格式错误，姓名只能是英文或中文！'));
			} else if (value.trim() === '') {
				callback(new Error('“姓名为空！'));
			} else if (value.trim().length > 16 || value.trim().length < 2) {
				callback(new Error('姓名格式错误，姓名长度为2-16位！'));
			} else {
				callback();
			}
		};
		return {
			clickedSubmit: false,
			excelUrl: importAccountTemplateUrl,
			uploadFileList: [],
			uploadFileConfig: getUploadFileConfig(),
			loading: false,
			addUserData: {
				nameRules: [
					{ required: true, message: '姓名不能为空', trigger: ['blur', 'change'] },
					{ validator: validateAgentName, trigger: 'blur' }
				]
			},
			showDialog: false,
			allAccounts: [],
			allLabels: [],
			allRoles: [],
			allGroups: [],
			importAccountList: []
		};
	},
	watch: {
		show(newVal) {
			this.showDialog = newVal;
			if (newVal) {
				this.refreshPreData();
			}
		}
	},
	computed: {
		...mapState({
			loginInfo: state => state.login.userInfo
		})
	},
	mounted() {},
	methods: {
		reset() {
			this.uploadFileList = [];
			this.$refs.uploadRef.clearFiles();
			this.clickedSubmit = false;
			this.loading = false;
		},
		removeUploadFile(e) {
			this.reset();
			e.stopPropagation();
		},
		refreshPreData() {
			getUserList({ page: 1, pageSize: Number.MAX_SAFE_INTEGER, role: 'agent' })
				.then(res => {
					// userName 不能重复
					this.allAccounts = res.data.data.data || [];
				})
				.catch(error => {
					this.$message.error('获取账号列表失败！');
					console.log('getUserList() failed:', error);
				});
			getBizLabelList({ page: 1, pageSize: Number.MAX_SAFE_INTEGER })
				.then(res => {
					// name 不重复时，取 id
					this.allLabels = res.data.data.data || [];
				})
				.catch(error => {
					this.$message.error('获取标签列表失败！');
					console.log('getBizLabelList() failed:', error);
				});
			getRoleList()
				.then(res => {
					// name 不重复时，取 id
					this.allRoles = res.data.data || [];
				})
				.catch(error => {
					this.$message.error('获取角色列表失败！');
					console.log('getRoleList() failed:', error);
				});
			getAllGroups()
				.then(res => {
					// name 不重复时，取 id
					this.allGroups = res.data || [];
				})
				.catch(error => {
					this.$message.error('获取分组列表失败！');
					console.log('getAllGroups() failed:', error);
				});
		},
		submit(event) {
			forceBtnBlur(event);
			if (this.uploadFileList.length > 0) {
				this.reset();
				importAccounts(this.importAccountList)
					.then(res => {
						this.$emit('importAccountsSuccess');
						this.$message.success('导入成功！');
					})
					.catch(error => {
						this.refreshPreData();
						console.log('importAccount failed:', error);
						switch (error.code + '') {
							case '1301':
								this.$message.error(`参数错误！`);
								break;
							case '1004':
								this.$message.error(`密码校验失败！`);
								break;
							case '1101':
								if (error.data.labelIds?.length > 0) {
									let labelNames = [];
									error.data.labelIds.forEach(value => {
										for (let i = 0; i < this.allLabels.length; i++) {
											if (this.allLabels[i].id === value) {
												if (!labelNames.includes(this.allLabels[i].name)) {
													labelNames.push(this.allLabels[i].name);
												}
												break;
											}
										}
									});
									this.$message.error(`<${labelNames}>标签与系统不匹配！！`);
								} else if (error.data.groupIds?.length > 0) {
									let groupNames = [];
									error.data.groupIds.forEach(value => {
										for (let i = 0; i < this.allGroups.length; i++) {
											if (this.allGroups[i].id === value) {
												if (!groupNames.includes(this.allGroups[i].name)) {
													groupNames.push(this.allGroups[i].name);
												}
												break;
											}
										}
									});
									this.$message.error(`<${groupNames}>分组与系统不匹配！`);
								} else {
									this.$message.error(`系统中不存在分组或者标签！`);
								}
								break;
							case '1002':
								this.$message.error(`用户名<${error.data.userNames}>已经存在！`);
								break;
							case '7001':
								this.$message.error(`密码强度不足，密码长度至少8个字符，且必须包含大、小写字母、数字、特殊字符`);
								break;
							case '7002':
								this.$message.error(`密码不能包含您的用户名和姓名，请重新设置！`);
								break;
							case '7003':
								this.$message.error(`密码不能包含您姓名的拼音部分，请重新设置！`);
								break;
							case '7004':
								this.$message.error(`密码不能包含两个以上连续的数字或字母，请重新设置！`);
								break;
							case '7005':
								this.$message.error(`密码不能包含两个以上重复的数字或字母，请重新设置！`);
								break;
							case '7006':
								this.$message.error(`您设置的密码属于规律键盘密码，存在安全风险，禁止使用，请重新设置！`);
								break;
							case '7007':
								this.$message.error(`您设置的密码属于特殊弱密码，禁止使用，请重新设置！`);
								break;
							default:
								this.$message.error(error);
								break;
						}
					});
			} else {
				this.clickedSubmit = true;
				// this.$message.warning('请先上传 Excel 文件！')
			}
		},
		cancel() {
			this.reset();
			this.$emit('close');
		},
		createAgent(info) {
			const { userName, pass, role } = info;
			const roleItem = this.roleList.find(item => item.name === role);
			const isRobot = roleItem.name === '机器人坐席';
			let data = {};
			data.userName = userName;
			data.org = this.loginInfo.org;
			data.role = 'agent';
			data.pwd = isRobot ? 'zealcomm@ivcs_robot2021' : pass;
			data.isRobot = isRobot;
			data.profile = {
				isRobot: isRobot
			};
			data.roleId = roleItem.id;
			createAccount(data)
				.then(res => {
					if (res.status === 200) {
						this.$message.success('创建坐席成功！');
						// this.$refs['formRef'].resetFields();
						this.reset();
						this.$emit('create');
					} else {
						this.$message.warning('创建坐席失败');
					}
				})
				.catch(err => {
					this.$requestApiError(err, () => {
						this.$router.push('/');
					});
					// this.$message.warning('创建坐席失败');
				});
		},
		onRemoveUploadFile(file, fileList) {
			this.uploadFileList = [];
		},
		dragToUpload(e) {
			console.log('drop event:', e.dataTransfer.files[0]);
			this.onFileChange({ raw: e.dataTransfer.files[0], size: e.dataTransfer.files[0].size, name: e.dataTransfer.files[0].name }, null);
		},
		onAnotherFile(files, fileList) {
			if (this.uploadFileList[0].name === files[0].name && this.uploadFileList[0].lastModified === files[0].lastModified && this.uploadFileList[0].size === files[0].size) {
				// 是之前上传的文件再做处理
			} else {
				this.onFileChange({ raw: files[0], size: files[0].size, name: files[0].name }, null);
			}
		},
		onFileChange(file, fileList) {
			this.loading = true;
			const isExcel = file.raw.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet';
			if (!isExcel) {
				this.$message.error('格式不正确，仅支持Excel文件导入！');
				this.reset();
				return;
			}
			if (file.size > excelMaxSize) {
				const size = Math.floor(excelMaxSize / 1024 / 1024);
				this.$message.error(`请上传小于${size}M的文件！`);
				this.reset();
				return;
			}
			// 这里稍微延迟一下，让 loading 效果展示出来，否则在读取比较耗时的 Excel 文件时，loading 效果有几率展示不出来
			setTimeout(() => {
				let fileReader = new FileReader();
				fileReader.readAsBinaryString(file.raw);
				fileReader.onload = e => {
					// 读取 excel 中的数据
					let data = e.target.result;
					let workbook = XLSX.read(data, { type: 'binary' });
					let sheetNames = workbook.SheetNames;
					if (sheetNames.length !== 1) {
						this.$message.error('Excel 中只允许有一张表');
						this.reset();
						return;
					}
					// 存储 excel 中表格的表头名称，必须保证一致
					let tempKeys = ['用户名', '角色', '姓名', '分组', '标签', '账号状态'];
					let firstSheet = XLSX.utils.sheet_to_json(workbook.Sheets[sheetNames[0]], { blankrows: true, defval: '' });
					if (firstSheet.length === 0) {
						this.$message.error('Excel 中没有数据！');
						this.reset();
						return;
					}
					if (firstSheet.length > 1000) {
						this.$message.error('Excel 中最大只允许 1000 条数据！');
						this.reset();
						return;
					}
					// 表格所有字段的校验结果
					let checkResult = '';
					// 校验真实姓名
					let validateName = value => {
						value += '';
						if (value.replace(FormReg.reg19, '').length !== 0) {
							return '姓名格式错误，姓名只能是英文或中文！';
						} else if (value.trim() === '') {
							return '姓名不能为空！';
						} else if (value.trim().length > 16 || value.trim().length < 2) {
							return '姓名格式错误，姓名长度为2-16位！';
						} else {
							return '';
						}
					};
					// 校验用户名
					let validateUserName = value => {
						value += '';
						if (value.replace(FormReg.reg3, '').length !== 0) {
							return '格式错误，用户名只能是英文、数字以及 - @ ~ _ 等特殊字符!';
						} else if (value.trim() === '') {
							return '格式错误，用户名不能为空!';
						} else if (value.trim().length > 16 || value.trim().length < 3) {
							return '格式错误， 用户名长度为 3 - 16 位!';
						} else {
							return '';
						}
						// TODO: 用户名与系统已有坐席用户名重复时提示“<xx>用户名与系统中重复！”
					};
					// 校验 excel 表头是否符合格式
					let keys = Object.keys(firstSheet[0]);
					for (let i = 0; i < tempKeys.length; i++) {
						if (tempKeys[i] !== keys[i]) {
							checkResult += `表单第 ${String.fromCharCode(i + 65)} 列，应该为 ${tempKeys[i]}<br />`;
						}
					}
					// excel 符合规范后再校验每列
					if (checkResult.length === 0) {
						// 统一处理每列的错误，result 校验结果
						let handleCheckResult = (row, col, result) => {
							if (result.length !== 0) {
								checkResult += `表单第 ${row + 2} 行, 第 ${String.fromCharCode(col + 65)} 列，${result}<br />`;
							}
						};
						// 重复的用户名
						let dulplicatedUserNames = {};
						// 提交的账号信息列表
						this.importAccountList = [];
						// 遍历表格中每行的信息
						firstSheet.forEach((value, index) => {
							let isBlankRow = true;
							for (let i = 0; i < tempKeys.length; i++) {
								if (value[tempKeys[i]]) {
									isBlankRow = false;
									break;
								}
							}
							if (!isBlankRow) {
								// 需要提交到服务器的表格每行的信息
								let accountData = {};
								this.importAccountList.push(accountData);
								for (let i = 0; i < tempKeys.length; i++) {
									switch (tempKeys[i]) {
										case '用户名':
											let userName = value[tempKeys[i]] + '';
											accountData.userName = userName;
											if (userName.length > 0) {
												dulplicatedUserNames[userName] = dulplicatedUserNames[userName] || [];
												dulplicatedUserNames[userName].push(index + 2);
											}
											handleCheckResult(index, i, validateUserName(userName));
											break;
										case '角色':
											let role = value[tempKeys[i]];
											// 不是机构管理员不能导入管理员角色
											if (!this.isOrgAdmin) {
												if ('管理员' === role) {
													handleCheckResult(index, i, `不能是管理员！`);
												}
											}
											if ('机器人坐席' === role) {
												handleCheckResult(index, i, `不能是机器人坐席！`);
											} else if (!role) {
												handleCheckResult(index, i, `角色不能为空！`);
											} else {
												for (let i = 0; i < this.allRoles.length; i++) {
													if (this.allRoles[i].name === role) {
														accountData.roleId = this.allRoles[i].id;
													}
												}
												if (!accountData.roleId) {
													handleCheckResult(index, i, `角色&lt;${role}&gt;不存在！`);
												}
											}
											break;
										case '姓名':
											accountData.fullName = value[tempKeys[i]];
											handleCheckResult(index, i, validateName(value[tempKeys[i]]));
											break;
										case '密码':
											// accountData.pwd = value[tempKeys[i]];
											// handleCheckResult(index, i, getValidatePassWordResult(value[tempKeys[i]]));
											break;
										case '分组':
											// 将分组名转换成分组 id 列表
											accountData.groupIds = [];
											let groupNames =
												value[tempKeys[i]]
													?.toString()
													.replace(/，|；|;/g, ',')
													.split(',') || [];
											if (groupNames.length > 10) {
												handleCheckResult(index, i, '分组超过最大限制10个！');
											} else {
												// 不存在于系统中的分组列表
												let notExistedGroup = [];
												// 重复的分组列表
												let dulplicatedGroup = [];
												// 筛选重复的分组和不存在于系统中的分组
												groupNames.forEach(value => {
													if (value) {
														let existedGroup = false;
														for (let i = 0; i < this.allGroups.length; i++) {
															if (this.allGroups[i].name === value) {
																existedGroup = true;
																if (accountData.groupIds.includes(this.allGroups[i].id)) {
																	if (!dulplicatedGroup.includes(value)) {
																		dulplicatedGroup.push(value);
																	}
																} else {
																	accountData.groupIds.push(this.allGroups[i].id);
																}
																break;
															}
														}
														if (!existedGroup) {
															notExistedGroup.push(value);
														}
													}
												});
												if (dulplicatedGroup.length > 0) {
													handleCheckResult(index, i, `分组&lt;${dulplicatedGroup}&gt;重复！`);
												}
												if (notExistedGroup.length > 0) {
													handleCheckResult(index, i, `分组&lt;${notExistedGroup}&gt;不存在！`);
												}
											}
											break;
										case '标签':
											// 将标签名转换为标签 id 列表
											accountData.labelIds = [];
											let labels =
												value[tempKeys[i]]
													?.toString()
													.replace(/，|；|;/g, ',')
													.split(',') || [];
											if (labels.length > 10) {
												handleCheckResult(index, i, '标签超过最大限制10个！');
											} else {
												// 不存在于系统中的标签
												let notExistedLabel = [];
												// 重复的标签
												let dulplicatedLabel = [];
												// 筛选重复的标签和不存在于系统中的标签
												labels.forEach(value => {
													if (value) {
														let existedLabel = false;
														for (let i = 0; i < this.allLabels.length; i++) {
															if (this.allLabels[i].name === value) {
																existedLabel = true;
																if (accountData.labelIds.includes(this.allLabels[i].id)) {
																	if (!dulplicatedLabel.includes(value)) {
																		dulplicatedLabel.push(value);
																	}
																} else {
																	accountData.labelIds.push(this.allLabels[i].id);
																}
																break;
															}
														}
														if (!existedLabel) {
															notExistedLabel.push(value);
														}
													}
												});
												if (dulplicatedLabel.length > 0) {
													handleCheckResult(index, i, `标签&lt;${dulplicatedLabel}&gt;重复！`);
												}
												if (notExistedLabel.length > 0) {
													handleCheckResult(index, i, `标签&lt;${notExistedLabel}&gt;不存在！`);
												}
											}
											break;
										case '账号状态':
											let accountState = value[tempKeys[i]] + '';
											accountData.isDisabled = accountState === '0' ? 'Y' : 'N';
											if (accountState.length > 0 && accountState !== '0' && accountState !== '1') {
												handleCheckResult(index, i, `账号状态只能为 0 或 1`);
											}
											break;
									}
								}
							}
						});
						// 检测重复用户名的结果
						let dulplicatedUserNamesResult = '';
						Object.keys(dulplicatedUserNames).forEach((value, index) => {
							let rows = dulplicatedUserNames[value];
							if (rows?.length > 1) {
								dulplicatedUserNamesResult += `表单中第 ${rows} 行，<${value}>用户名重复！<br />`;
							}
						});
						// 拼接最终的校验结果
						if (dulplicatedUserNamesResult.length > 0) {
							checkResult = `<b>用户名重复：</b><br />${dulplicatedUserNamesResult}<br />${checkResult.length > 0 ? `<b>格式错误：</b><br />` : ''}${checkResult}`;
						}
					} else {
						checkResult = '<b>请勿修改表格表头信息，请重新编辑后再导入！</b><br />' + checkResult;
					}
					// 关闭校验动画
					this.loading = false;
					// 处理校验结果
					if (checkResult.length === 0) {
						this.uploadFileList = [{ name: file.name, lastModified: file.raw.lastModified, size: file.size }];
						this.$message.success('数据校验成功！');
					} else {
						this.reset();
						this.$alert(`<div style="overflow-y:scroll;overflow-x:hidden;max-height:300px">${checkResult}</div>`, 'Excel 检测失败', {
							confirmButtonText: '确定',
							dangerouslyUseHTMLString: true
						});
					}
				};
			}, 100);
		},
		onUploadProgress(event, file, fileList) {
			fixUploadPercent(this, event, file, fileList);
		}
	}
};
</script>

<style lang="less">
.account {
	.btns {
		text-align: right;
	}
	.head {
		padding: 20px 0;
	}
	.el-avatar > img {
		width: 100%;
	}
}
</style>

<style lang="less" scoped>
.download-button {
	width: 80px;
	height: 28px;
	background: #ffffff;
	border: 1px solid #6384fe;
	border-radius: 4px;
	font-size: 12px;
	font-weight: 400;
	color: #6384fe;
	line-height: 28px;
	padding: 0;
}
.before-upload-box {
	display: flex;
	flex-direction: column;
	height: 100%;
	justify-content: center;
	.upload-text {
		margin-top: 10px;
	}
}
.upload-box {
	display: flex;
	align-items: center;
	height: 100%;
	.upload-items {
		display: flex;
		flex-direction: row;
		align-items: center;
		padding-left: 10px;
		padding-right: 10px;
		justify-content: center;
		width: 100%;
		.excel-img {
			margin: 0 0 0 0;
		}
		.excel-name {
			margin: 0 0 0 10px;
		}
		.excel-remove {
			padding: 10px 10px 10px 10px;
		}
	}
}
</style>
